/* --- CURRENT WORKING DIRECTORY --- */
%GLOBAL CWD; %LET CWD = %SUBSTR(%SYSGET(SAS_EXECFILEPATH), 1, %EVAL(%LENGTH(%SYSGET(SAS_EXECFILEPATH)) - %LENGTH(%SYSGET(SAS_EXECFILENAME)) - 1));

/* --- PROJECT-SPECIFIC TOOLS --- */
%MACRO LOADPTOOLS; %IF %SYSFUNC(FILEEXIST("&CWD\PTOOLS.SAS")) %THEN %INCLUDE "&CWD\PTOOLS.SAS"; %MEND; %LOADPTOOLS;

/* *****************************************************************************
%INM: A VERSION OF THE 'IN' OPERATOR THAT WORKS FOR MACRO VARIABLES.
-SYNTAX:
	THE CORRESPONDING CODE TO
		IF VAR IN (1, 2, 3, 4)
	IS
		%IF %INM(&VAR 1, 2, 3, 4)

	YOU CAN EASILY CHECK IF A VARIABLE IS EITHER A DIGIT OR A CHARACTER. FOR 
	DIGITS, USE
		%IF %INM(&VAR #)
	AND FOR CHARACTERS, USE
		%IF %INM(&VAR ##)
			
-NOTES:
	THE CODE IS NOT DESIGNED TO CHECK FOR SPECIAL CHARACTERS SUCH AS COMMAS.

	COMMA DELIMITATION IS OPTIONAL. SEE THE DOCUMENTATION FOR %QSCAN.		

	YOU CAN CREATE YOUR OWN PERSISTENT CLASSES. SEE THE IN-CODE
	DOCUMENTATION.
*******************************************************************************/
%MACRO INM/PARMBUFF;
	%LET INM_ARGS = &SYSPBUFF;

	%LET INM_QRY = %QSCAN(&INM_ARGS, 1);
	%LET INM_INDX1 = 2;
	%LET INM_ARG = %QSCAN(&INM_ARGS, &INM_INDX1);	
	
	/* DEFINE PERSISTENT CLASSES */
	%LET INM_DIGITS = 0 1 2 3 4 5 6 7 8 9;	
	%LET INM_CHARS = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z;	

	/* CHECK FOR PERSISTENT CLASS FLAGS */
	%IF &INM_ARG EQ # %THEN %DO;		
		%LET INM_ARGS = &INM_DIGITS;
		%LET INM_INDX1 = 1;
		%LET INM_ARG = %QSCAN(&INM_ARGS, &INM_INDX1);		
	%END;
	%IF &INM_ARG EQ ## %THEN %DO;
		%LET INM_ARGS = &INM_CHARS;
		%LET INM_INDX1 = 1;
		%LET INM_ARG = %QSCAN(&INM_ARGS, &INM_INDX1);
	%END;	
	%DO %WHILE (&INM_ARG NE);
		
		%IF &INM_QRY EQ &INM_ARG %THEN %DO;
			1
			%GOTO EXIT;
		%END;		
		%LET INM_INDX1 = %EVAL(&INM_INDX1 + 1);
		%LET INM_ARG = %QSCAN(&INM_ARGS, &INM_INDX1);		
	%END;
	0
%EXIT:
%MEND;

/* *****************************************************************************
%UP: INCREMENT A MACRO VARIABLE.
-SYNTAX:
	TO INCREMENT A MACRO VARIABLE BY 1, USE
		%UP(VARNAME);
	TO SPECIFY THE INCREMENT AMOUNT, USE
		%UP(VARNAME INCREMENTAMOUNT);
-NOTES:
	DO NOT INCLUDE & IN THE VARNAME

	USE %DOWN TO DECREMENT
*******************************************************************************/
%MACRO UP/PARMBUFF;	
	%LET UP_ARGS = &SYSPBUFF;
	%LET UP_VAR = %SCAN(&UP_ARGS, 1);
	%LET UP_INC = 1;
	%IF %SCAN(&UP_ARGS, 2) NE %THEN %LET UP_INC = %SCAN(&UP_ARGS, 2);
	%LET &UP_VAR = %EVAL(%UNQUOTE(%STR(&)&UP_VAR) + &UP_INC);
%MEND;

/* *****************************************************************************
%DOWN: DECREMENT A MACRO VARIABLE.
-SYNTAX:
	TO DECREMENT A MACRO VARIABLE BY 1, USE
		%DOWN(VARNAME);
	TO SPECIFY THE DECREMENT AMOUNT, USE
		%DOWN(VARNAME DECREMENTAMOUNT);	
-NOTES:
	DO NOT INCLUDE & IN THE VARNAME

	USE %UP TO INCREMENT
*******************************************************************************/
%MACRO DOWN/PARMBUFF;	
	%LET DOWN_ARGS = &SYSPBUFF;
	%LET DOWN_VAR = %SCAN(&DOWN_ARGS, 1);
	%LET DOWN_INC = 1; 
	%IF %SCAN(&DOWN_ARGS, 2) NE %THEN %LET DOWN_INC = %SCAN(&DOWN_ARGS, 2);
	%LET &DOWN_VAR = %EVAL(%UNQUOTE(%STR(&)&DOWN_VAR) - &DOWN_INC);		
%MEND;

/* *****************************************************************************
%RANK: RETURN A CHARACTER'S ASCII CODE
-SYNTAX:
	TO FIND THE RANK OF 'A' (65), USE
		%RANK(A)
*******************************************************************************/
%MACRO RANK(RANK_ARG);
	%SYSFUNC(RANK(&RANK_ARG))
%MEND;

/* *****************************************************************************
%COUNTWORDS: RETURN THE NUMBER OF WORDS IN A MACRO VARIABLE
-SYNTAX:
	TO FIND THE NUMBER OF WORDS, USE
		COUNTWORDS(THIS IS A TEST)
*******************************************************************************/
%MACRO COUNTWORDS(ARG);
	%LET INDX1 = 1;
	%LET WORD = %SCAN(&ARG, &INDX1);
	%DO %WHILE (&WORD NE);
		%LET INDX1 = %EVAL(&INDX1 + 1);
		%LET WORD = %SCAN(&ARG, &INDX1);
	%END;
	%EVAL(&INDX1 - 1)
%MEND;

/* *****************************************************************************
%COUNTMONTHS: RETURN THE NUMBER OF MONTHS BETWEEN TWO PERIODS
-SYNTAX:
	SUPPLY ARGUMENTS IN DATE6 FORMAT LIKE THIS
		%COUNTMONTHS(200309, 200412)
-NOTES:
	THE DATE RANGE IS INCLUSIVE. FOR EXAMPLE, THE MACRO INTERPRETS
	200309 - 200409 AS COVERING 13 MONTHS.	
*******************************************************************************/
%MACRO COUNTMONTHS(COUNTMONTHS_DATE1, COUNTMONTHS_DATE2);
	%IF %LENGTH(&COUNTMONTHS_DATE1) NE 6 OR %LENGTH(&COUNTMONTHS_DATE2) NE 6 OR %EVAL(%SUBSTR(&COUNTMONTHS_DATE1, 5, 2) > 12) OR %EVAL(%SUBSTR(&COUNTMONTHS_DATE2, 5, 2) > 12) %THEN %DO;
		;%PUT ERROR: INVALID ARGUMENTS PASSED TO %NRSTR(%NUMMONTHS);
		%ABORT;
	%END;
	%ELSE %DO;
		%LET COUNTMONTHS_MONTHS = %EVAL(1 + %SUBSTR(&COUNTMONTHS_DATE2, 5, 2) - %SUBSTR(&COUNTMONTHS_DATE1, 5, 2) + 12 * (%SUBSTR(&COUNTMONTHS_DATE2, 1, 4) - %SUBSTR(&COUNTMONTHS_DATE1, 1, 4));
		&COUNTMONTHS_MONTHS		
	%END;	
%MEND;

